﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Commands;
using Commands.Commands;
using Microsoft.Practices.Unity;
using Moq;
using NUnit.Framework;

namespace CommandsTests
{
    [TestFixture]
    class ParametersHelperTest
    {
        [Test]
        public void CreateMethodParameters_ParameterWithoutNames()
        {
            var list = new List<ParameterInfo>();
            list.Add(_GetParameterInfo(typeof(int), false, "m"));

            var command = new Command("asd");
            var value = "123";
            command.Parameters.Add(value);

            var helper = _NewParametersHelper();
            var parameters = helper.CreateMethodParameters(list, command, new UnityContainer());

            Assert.NotNull(parameters);

            Assert.AreEqual(1, parameters.Count());
            Assert.AreEqual(typeof(int), parameters.First().GetType());
            Assert.AreEqual(123, parameters.First());
        }

        [Test]
        public void CreateMethodParameters_SeveralParametersWithoutNames()
        {
            var list = new List<ParameterInfo>();
            list.Add(_GetParameterInfo(typeof(string), false, "a"));
            list.Add(_GetParameterInfo(typeof(bool), false, "c"));
            list.Add(_GetParameterInfo(typeof(int), false, "m"));

            var command = new Command("asd");
            var value1 = "asdasd";
            command.Parameters.Add(value1);
            var value3 = "true";
            command.Parameters.Add(value3);
            var value = "123";
            command.Parameters.Add(value);

            var helper =  _NewParametersHelper();
            var parameters = helper.CreateMethodParameters(list, command, new UnityContainer());

            Assert.NotNull(parameters);

            Assert.AreEqual(list.Count, parameters.Count());

            Assert.AreEqual(typeof(string), parameters.First().GetType());
            Assert.AreEqual(value1, parameters.First());

            Assert.AreEqual(typeof(bool), parameters.ElementAt(1).GetType());
            Assert.AreEqual(true, parameters.ElementAt(1));

            Assert.AreEqual(typeof(int), parameters.Last().GetType());
            Assert.AreEqual(123, parameters.Last());
        }

        [Test]
        public void CreateMethodParameters_SeveralParametersWithoutNamesNotAllParamaters()
        {
            var list = new List<ParameterInfo>();
            list.Add(_GetParameterInfo(typeof(string), true, "a"));
            list.Add(_GetParameterInfo(typeof(bool), false, "c"));
            list.Add(_GetParameterInfo(typeof(int), false, "m"));

            var command = new Command("asd");
            var value1 = "asdasd";
            command.Parameters.Add(value1);
            var value3 = "true";
            command.Parameters.Add(value3);

            var helper =  _NewParametersHelper();
            var parameters = helper.CreateMethodParameters(list, command, new UnityContainer());

            Assert.NotNull(parameters);

            Assert.AreEqual(list.Count, parameters.Count());

            Assert.AreEqual(typeof(string), parameters.First().GetType());
            Assert.AreEqual(value1, parameters.First());

            Assert.AreEqual(typeof(bool), parameters.ElementAt(1).GetType());
            Assert.AreEqual(true, parameters.ElementAt(1));

            Assert.AreEqual(typeof(int), parameters.Last().GetType());
            Assert.AreEqual(default(int), parameters.Last());
        }

        [Test]
        public void CreateMethodParameters_ParametersWithName()
        {
            var list = new List<ParameterInfo>();
            list.Add(_GetParameterInfo(typeof(int), false, "m"));
            list.Add(_GetParameterInfo(typeof(string), false, "a"));
            list.Add(_GetParameterInfo(typeof(bool), false, "c"));

            var command = new Command("asd");
            var value1 = "asdasd";
            command.Parameters.Add("a", value1);
            var value3 = "true";
            command.Parameters.Add("c", value3);
            var value = "123";
            command.Parameters.Add("m", value);

            var helper =  _NewParametersHelper();
            var parameters = helper.CreateMethodParameters(list, command, new UnityContainer());

            Assert.NotNull(parameters);

            Assert.AreEqual(list.Count, parameters.Count());

            Assert.AreEqual(typeof(int), parameters.First().GetType());
            Assert.AreEqual(123, parameters.First());

            Assert.AreEqual(typeof(string), parameters.ElementAt(1).GetType());
            Assert.AreEqual(value1, parameters.ElementAt(1));

            Assert.AreEqual(typeof(bool), parameters.Last().GetType());
            Assert.AreEqual(true, parameters.Last());
        }
        [Test]
        public void CreateMethodParameters_ParametersWithNameNotAllParameters()
        {
            var list = new List<ParameterInfo>();
            list.Add(_GetParameterInfo(typeof(int), false, "m"));
            list.Add(_GetParameterInfo(typeof(string), true, "a"));
            list.Add(_GetParameterInfo(typeof(bool), false, "c"));

            var command = new Command("asd");
            var value1 = "asdasd";
            command.Parameters.Add("a", value1);
            var value = "123";
            command.Parameters.Add("m", value);

            var helper =  _NewParametersHelper();
            var parameters = helper.CreateMethodParameters(list, command, new UnityContainer());

            Assert.NotNull(parameters);

            Assert.AreEqual(list.Count, parameters.Count());

            Assert.AreEqual(typeof(int), parameters.First().GetType());
            Assert.AreEqual(123, parameters.First());

            Assert.AreEqual(typeof(string), parameters.ElementAt(1).GetType());
            Assert.AreEqual(value1, parameters.ElementAt(1));

            Assert.AreEqual(typeof(bool), parameters.Last().GetType());
            Assert.AreEqual(default(bool), parameters.Last());
        }

        [Test]
        public void CreateMethodParameters_DefaultParameterOnlyOneParameter()
        {
            var list = new List<ParameterInfo>();
            list.Add(_GetParameterInfo(typeof(int), true, "m"));

            var command = new Command("asd");
            var value = "123";
            command.Parameters.DefaultParameter = value;

            var helper =  _NewParametersHelper();
            var parameters = helper.CreateMethodParameters(list, command, new UnityContainer());

            Assert.NotNull(parameters);

            Assert.AreEqual(1, parameters.Count());

            Assert.AreEqual(typeof(int), parameters.First().GetType());
            Assert.AreEqual(123, parameters.Last());
        }

        [Test]
        public void CreateMethodParameters_DefaultParameterSeveralParameters()
        {
            var list = new List<ParameterInfo>();
            list.Add(_GetParameterInfo(typeof(int), true, "m"));
            list.Add(_GetParameterInfo(typeof(string), false, "a"));
            list.Add(_GetParameterInfo(typeof(bool), false, "c"));
            list.Add(_GetParameterInfo(typeof(char), false, "char"));
            list.Add(_GetParameterInfo(typeof(float), false, "dd"));

            var command = new Command("asd");
            var value = "123";
            command.Parameters.DefaultParameter = value;

            var helper =  _NewParametersHelper();
            var parameters = helper.CreateMethodParameters(list, command, new UnityContainer());

            Assert.NotNull(parameters);

            Assert.AreEqual(list.Count, parameters.Count());

            Assert.AreEqual(typeof(int), parameters.First().GetType());
            Assert.AreEqual(123, parameters.First());

            Assert.AreEqual(null, parameters.ElementAt(1));

            Assert.AreEqual(typeof(bool), parameters.ElementAt(2).GetType());
            Assert.AreEqual(default(bool), parameters.ElementAt(2));

            Assert.AreEqual(typeof(char), parameters.ElementAt(3).GetType());
            Assert.AreEqual(default(char), parameters.ElementAt(3));

            Assert.AreEqual(typeof(float), parameters.ElementAt(4).GetType());
            Assert.AreEqual(default(float), parameters.ElementAt(4));
        }

        private ParametersHelper _NewParametersHelper()
        {
            return new ParametersHelper(string.Empty, string.Empty);
        }

        private ParameterInfo _GetParameterInfo(Type type, bool required, params string[] name)
        {
            var mock = new Mock<ParameterInfo>();
            mock.Setup(x => x.ParameterType).Returns(type);
            var par = new ParameterAttribute("", type, required, name);
            mock.Setup(x => x.GetCustomAttributes(It.IsAny<Type>(), It.IsAny<bool>())).Returns(new object[]{par});

            return mock.Object;
        }
    }
}
